/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.lestr.genver.data.serialization;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;

/**
 *
 * @author PIBONNIN
 */
public class Writer {


    private ArrayList<Object> alreadyWritedObjects;


    private BeanSerializerAdapter defaultBeanSerializerAdapter;


    private byte[] buffer;


    private OutputStream output;


    public Writer() {

        buffer = new byte[8];
        output = null;
        alreadyWritedObjects = new ArrayList<Object>();
        defaultBeanSerializerAdapter = new BeanSerializerAdapter();

    }// END Constructor


    public void open(OutputStream output) throws IOException {

        close();

        this.output = output;

    }// FIN Method open


    public void close() throws IOException {

        alreadyWritedObjects.clear();
        output = null;

    }// FIN Method close


    public void write(Class<?> type,
                      Object targetObject) throws IOException {

        int index;

        if (type.getName().equals("java.lang.Float")
            || type.getName().equals("float"))
            writeFloat((Float) targetObject);
        else if (type.getName().equals("java.lang.Double")
                 || type.getName().equals("double"))
            writeDouble((Double) targetObject);
        else if (type.getName().equals("java.lang.Short")
                 || type.getName().equals("short"))
            writeShort((Short) targetObject);
        else if (type.getName().equals("java.lang.Integer")
                 || type.getName().equals("int"))
            writeInt((Integer) targetObject);
        else if (type.getName().equals("java.lang.Long")
                 || type.getName().equals("long"))
            writeLong((Long) targetObject);
        else if (type.getName().equals("java.lang.Byte")
                 || type.getName().equals("byte"))
            writeByte((Byte) targetObject);
        else if (type.getName().equals("java.lang.String"))
            writeString((String) targetObject);
        else if (type.getName().equals("java.lang.Character")
                 || type.getName().equals("char"))
            writeChar((Character) targetObject);
        else if (type.getName().equals("java.lang.Boolean")
                 || type.getName().equals("boolean"))
            writeBoolean((Boolean) targetObject);
        else if (type.getName().equals("java.lang.Void")
                 || type.getName().equals("void"))
            writeByte((byte) 0);
        else {

            index = alreadyWritedObjects.indexOf(targetObject);

            writeInt(index);

            if (index != -1) {

                alreadyWritedObjects.add(targetObject);

                if (targetObject instanceof ISerializable)
                    ((ISerializable) targetObject).save(this);
                else {
                    defaultBeanSerializerAdapter.setBeanSerializer(new DefaultBeanSerializer(type));
                    defaultBeanSerializerAdapter.write(this, targetObject);
                }

            }

        }

    }// END Method Write


    private void writeByte(byte val) throws IOException {

        buffer[0] = val;

        output.write(buffer, 0, 1);

    }// END Method writeBoolean


    private void writeBoolean(boolean val) throws IOException {

        buffer[0] = (byte) (val ? 1 : 0);

        output.write(buffer, 0, 1);

    }// END Method writeBoolean


    private void writeChar(char val) throws IOException {

        buffer[1] = (byte) val;
        buffer[0] = (byte) (val >>> 8);

        output.write(buffer, 0, 2);

    }// END Method writeChar


    private void writeShort(short val) throws IOException {

        buffer[1] = (byte) val;
        buffer[0] = (byte) (val >>> 8);

        output.write(buffer, 0, 2);

    }// END Method writeShort


    private void writeInt(int val) throws IOException {

        buffer[3] = (byte) val;
        buffer[2] = (byte) (val >>> 8);
        buffer[1] = (byte) (val >>> 16);
        buffer[0] = (byte) (val >>> 24);

        output.write(buffer, 0, 4);

    }// END Method writeInt


    private void writeFloat(float val) throws IOException {

        int i = Float.floatToIntBits(val);

        buffer[3] = (byte) i;
        buffer[2] = (byte) (i >>> 8);
        buffer[1] = (byte) (i >>> 16);
        buffer[0] = (byte) (i >>> 24);

        output.write(buffer, 0, 4);

    }// END Method writeFloat


    private void writeLong(long val) throws IOException {

        buffer[7] = (byte) val;
        buffer[6] = (byte) (val >>> 8);
        buffer[5] = (byte) (val >>> 16);
        buffer[4] = (byte) (val >>> 24);
        buffer[3] = (byte) (val >>> 32);
        buffer[2] = (byte) (val >>> 40);
        buffer[1] = (byte) (val >>> 48);
        buffer[0] = (byte) (val >>> 56);

        output.write(buffer, 0, 8);

    }// END Method writeLong


    private void writeDouble(double val) throws IOException {

        long j = Double.doubleToLongBits(val);

        buffer[7] = (byte) j;
        buffer[6] = (byte) (j >>> 8);
        buffer[5] = (byte) (j >>> 16);
        buffer[4] = (byte) (j >>> 24);
        buffer[3] = (byte) (j >>> 32);
        buffer[2] = (byte) (j >>> 40);
        buffer[1] = (byte) (j >>> 48);
        buffer[0] = (byte) (j >>> 56);

        output.write(buffer, 0, 8);

    }// END Method writeDouble


    private void writeString(String val) throws IOException {

        byte[] utfBuffer = val.getBytes("UTF-8");
        writeInt(utfBuffer.length);
        output.write(utfBuffer);

    }// END Method writeString


}// END Class Writer


